using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using Zsa.Emulation.Render.Video;
using System.Windows.Forms;
using Zsa.Emulation.Config;

namespace Zsa.Emulation.Render
{
    /// <summary>
    /// Used to render everything.
    /// </summary>
    public class NesMachineRender
    {

        #region Fields

        #region Devices
        /// <summary>
        /// Renders the video.
        /// </summary>
        private Video.GraphicRender _GraphicsRender;

        /// <summary>
        /// The input device.
        /// </summary>
        private Input.ControllerDeviceManager _InputDevice;

        /// <summary>
        /// Renders the sound.
        /// </summary>
        private Audio.SoundDevice _SoundRender;
        #endregion Devices

        /// <summary>
        /// Surface area where graphics are rendered.
        /// </summary>
        private SurfaceAreaControl _SurfaceControl;

        /// <summary>
        /// The render settings.
        /// </summary>
        private NesConfigurationProfile _Config;

        /// <summary>
        /// The device
        /// </summary>
        private NesMachineHost _Machine;

        #endregion Fields


        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="T:DeviceRenderController"/> class.
        /// </summary>
        /// <param name="nesMachine">The nes machine.</param>
        /// <param name="config">The config.</param>
        internal NesMachineRender(NesMachineHost nesMachine, NesConfigurationProfile config)
        {
            if (nesMachine == null)
            {
                throw new ArgumentNullException("nesMachine");
            }
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            _Config = config;
            _Machine = nesMachine;
        }
        #endregion Constructor


        #region Methods

        /// <summary>
        /// Inits this instance.
        /// </summary>
        public void Init()
        {
            _SurfaceControl.Visible = true;

            _GraphicsRender = _Config.GraphicsSettings.CreateDevice();
            _GraphicsRender.NesHost = this._Machine;
            _GraphicsRender.SurfaceControl = _SurfaceControl;
            _GraphicsRender.InitDevice();

            _InputDevice = _Config.InputSettings.CreateDevice();
            _InputDevice.NesHost = this._Machine;
            _InputDevice.SurfaceControl = _SurfaceControl;
            _InputDevice.InitDevice();

            _SoundRender = _Config.AudioSettings.CreateDevice();
            _SoundRender.NesHost = this._Machine;
            _SoundRender.SurfaceControl = _SurfaceControl;
            _SoundRender.InitDevice();
        }

        /// <summary>
        /// Shutdowns this instance.
        /// </summary>
        internal void Shutdown()
        {
            if (_InputDevice != null)
            {
                _InputDevice.Dispose();
            }

            if (_GraphicsRender != null)
            {
                _GraphicsRender.Dispose();
            }

            if (_SoundRender != null)
            {
                _SoundRender.Dispose();
            }

            Control _Control = _SurfaceControl.Parent;
            _Control.Controls.Remove(_Control);
            _Control.Dispose();
        }

        /// <summary>
        /// Renders the output sound and audio.
        /// </summary>
        public void Render()
        {
            // Process controls.
            _InputDevice.ProcessControls();
            // Draws the screen.
            _GraphicsRender.DrawScreen();
            // Plays the sound.
            _SoundRender.RenderAudio();
        }



        /// <summary>
        /// Sets the control host.
        /// </summary>
        /// <param name="hostControl">The host control.</param>
        public Control SetControlHost(Control hostControl)
        {
            _SurfaceControl = CreateSurfaceControl(hostControl);
            return _SurfaceControl;

        }

        

        /// <summary>
        /// Sets the surface control.
        /// </summary>
        /// <param name="control">The control.</param>
        private SurfaceAreaControl CreateSurfaceControl(Control control)
        {
            // The control error message.
            const string _ControlErrorMessage = "The host control cannot contain any other controls !";

            // Temp var for the surface control.
            SurfaceAreaControl _SurfaceControlTemp = null;

            // Make sure the host is not null.
            if (control == null)
            {
                throw new ArgumentNullException("control");
            }

            // Check the child controls.
            if (control.Controls.Count > 1)
            {
                throw new ArgumentException(_ControlErrorMessage);
            }
            else if (control.Controls.Count == 1)
            {
                _SurfaceControlTemp = control.Controls[0] as SurfaceAreaControl;
                if (_SurfaceControlTemp == null)
                {
                    throw new ArgumentException(_ControlErrorMessage);
                }
            }
            else
            {
                // Create the surface area control.
                _SurfaceControlTemp = new SurfaceAreaControl(_Machine, _Config);
            }

            // Add the control.
            control.Controls.Add(_SurfaceControlTemp);
            // Fill the host control
            _SurfaceControlTemp.Location = new Point(0, 0);
            _SurfaceControlTemp.Dock = DockStyle.Fill;
            // Set the visiblity to false.
            _SurfaceControlTemp.Visible = false;
            return _SurfaceControlTemp;
        }
        #endregion Methods


        #region Properties
        /// <summary>
        /// Gets a value indicating whether this instance has surface.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has surface; otherwise, <c>false</c>.
        /// </value>
        public bool HasSurface
        {
            get
            {
                return _SurfaceControl != null;
            }
        }



        /// <summary>
        /// Gets the surface control.
        /// </summary>
        /// <value>The surface control.</value>
        internal SurfaceAreaControl SurfaceControl
        {
            get
            {
                return _SurfaceControl;
            }
        }

        #endregion Properties


    }
}
